<?php

namespace app\models\tableModel;

use app\entity\PageListEntity;
use app\models\cacheModel\AdminRoleModel as AdminRoleCacheModel;
use app\models\tableModel\base\AdminRole;
use app\service\tool\ConvertService;
use app\service\tool\FileService;
use app\service\tool\GenerateService;
use app\service\tool\UtilsService;
use yii\helpers\ArrayHelper;
use yii\helpers\Inflector;

/**
 * 角色管理[Model]
 * User: Administrator
 * Date: 1995/12/22
 * Time: 17:59
 */
class AdminRoleModel extends AdminRole
{

    /**
     * 状态 列表
     * @var array
     */
    private static $statusList = [
        [
            'key'   => 'disabled',
            'text'  => '已禁用',
            'value' => -1,
        ],
        [
            'key'   => 'open',
            'text'  => '已开启',
            'value' => 1,
        ]
    ];

    /**
     * 排序最大值
     * @var int
     */
    protected static $sortMax = 999999;
    /**
     * 排序最小值
     * @var int
     */
    protected static $sortMin = -999999;
    /**
     * 超管编号列表
     * @var array
     */
    protected static $superRoleId = [
        '1'
    ];


    /**
     * 规则验证
     * @return array
     */
    public function rules()
    {

        $parent     = parent::rules();        // 状态
        $statusList = array_column(self::getStatusList(), 'value');

        return ArrayHelper::merge($parent, [
            ['status', 'in', 'range' => $statusList, 'message' => '状态不合法'],
        ]);
    }

    /**
     * 重写label的 文字
     */
    public function attributeLabels()
    {
        return [
            'id'          => '编号',
            'name'        => '名称',
            'content'     => '描述',
            'update_time' => '更新时间',
            'status'      => '状态',
        ];
    }

    /**
     * 场景
     * @return array
     */
    public function scenarios()
    {

        $scenarios = parent::scenarios();
        return ArrayHelper::merge($scenarios, [
            [
                // 自定义场景 (无用请删除)
                'scUpdate' => [
                    'someAttributes'
                ]
            ]
        ]);
    }

    /**
     * 获取分页信息
     * @param $page
     * @param $limit
     * @param null $field
     * @return PageListEntity
     */
    public function getPaginate($page, $limit, $field = null)
    {

        // 条件
        $where = $this->where;

        // 当前页面计算
        $page = max($page - 1, 0);

        // 查找的 字段空的 就默认给列表
        if (!$field) $field = '*';


        // 基础[where]加载完毕
        $base = $this::find()->select($field)->where($where);

        // 数据的获取 分页等
        $list = $base->offset($page * $limit)
            ->limit($limit)
            ->asArray()->all();

        // 总条数
        $total = $base->count();

        // 格式化数据
        foreach ($list as $k => &$v) {

            // 更新时间
            if (!empty($v['update_time'])) {
                $v['update_time_text']   = date('Y-m-d H:i:s', $v['update_time']);
                $v['update_time_text_s'] = date('Y-m-d', $v['update_time']);
            }

            // 状态文本
            if (isset($v['status'])) {
                $v['status_text'] = self::getStatusText($v['status']);
            }

            // 内容转化下
            if (!empty($v['content'])) {
                $v['content'] = htmlspecialchars_decode($v['content']);
                $v['content'] = FileService::addHtmlImgDomain($v['content']);
            }

            // 权限 转[json]
            if (isset($v['auth']) && is_array(json_decode($v['auth'], true))) {
                $v['auth'] = json_decode($v['auth'], true);
            }

            // 权限 转[json]
            if (isset($v['menu']) && is_array(json_decode($v['menu'], true))) {
                $v['menu'] = json_decode($v['menu'], true);
            }
        }

        return PageListEntity::loadModel(compact('list', 'total'));
    }

    /**
     * 自定义过滤单条[where]
     *  ` 仅限于通过方法[loadWhere]传入的条件, 可以在其中自定义逻辑
     *  ` 保证最终返回值为一维数组[Yii2]通用条件格式[运算符, 字段名, 值]
     * @param $condition array [sql]查询条件, 严格遵循Yii多条件查询:[运算符, 字段名, 值]
     * @return array
     */
    public function whereCustomFilter($condition = [])
    {

        // // 例：字段为编号 —— 操作符使用[IN]
        // if ($field == 'id') {
        //     return ['IN', $condition[1], $value[2]];
        // }

        // 最终返回必须为数组：[运算符, 字段名, 值]，符合Yii2规范
        return $condition;
    }

    /**
     * 添加|保存
     * @param bool $runValidation 是否仅仅运行验证，不运行保存
     * @param string[]|null|string $attributeNames 仅仅保存某几个字段
     * @return bool
     */
    public function save($runValidation = false, $attributeNames = null)
    {

        ### 某些数据格式化
        $nowTime = time();
        // 添加的话要赋值一些初始数据
        if (empty($this->id)) {

            // 可以是走[mongoId]
            $this->id = GenerateService::newMongoId();
        }

        // 添加时间
        $this->update_time = $nowTime;

        // 内容解密下 - 防止加密多次
        $this->content = htmlspecialchars_decode($this->content);
        // 内容加密下
        $this->content = htmlspecialchars($this->content);
        // 内容取出图片域名
        $this->content = FileService::delHtmlImgDomain($this->content);
        // 权限如果是字符串 将转为数组
        if (ConvertService::isJson($this->auth)) {
            $this->auth = json_decode($this->getAttribute('auth'), true);
        }

        ### 检测
        if ($this->hasErrors() || !$this->validate($attributeNames)) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[管理员角色]操作模型]验证数据失败                ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $this->getAttributes(),
                $this->getErrors()
            ], 'db');
            return false;
        }

        ### 需要 && 执行保存
        if (!$runValidation && !parent::save(false, $attributeNames)) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[管理员角色]操作模型]保存数据失败                ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $this->getAttributes(),
                $this->getErrors()
            ], 'db');
            return false;
        }

        return true;
    }

    /**
     * 更新下角色的菜单
     *  `   根据现在角色数据库[auth]值比对出菜单列表保存到[menu]中    `
     *  `   根据数据库更新下缓存  `
     * @return null|array
     */
    public function getMenu()
    {

        // 权限空默认赋值
        /* @var string $auth */
        $auth = $this->getAttribute('auth');
        if (ConvertService::isJson($auth)) $auth = json_decode($auth, true);
        if (empty($auth)) $auth = [];

        // 路由列表
        /* @var array $auth */
        $urlList = ArrayHelper::getColumn($auth, 'url');

        // 角色菜单
        $menuList = AdminMenuModel::loadModel()->filterMenuByAuth($urlList,
            self::isSuper($this->id)
        );

        return $menuList;
    }

    /**
     * 更新某些字段
     * @param $condition
     * @param array $fieldVal
     * @return bool
     */
    public static function updateField($condition, $fieldVal = [])
    {

        // 验证字段
        $model = self::loadModel();
        $model->load($fieldVal, '');
        if (!$model->validate(array_keys($fieldVal))) {
            $error                          = UtilsService::getModelError($model->getErrors());
            self::$error_[$error['column']] = [$error['msg']];
            return false;
        }
        // 重新取值规则化后的特定值列表
        $fieldVal = $model->getAttributes(array_keys($fieldVal));

        $db = \Yii::$app->db->createCommand();

        try {

            $result = $db->update(self::tableName(), $fieldVal, $condition)->execute();

            // 否则成功
            return true;
        } catch (\Exception $error) {

            // 记录下错误日志
            \Yii::error([
                "````````````````````````````````````````````````````````",
                "``                      数据库错误                      ``",
                "`` 错误详情: 修改[指定字段]失败，{$error->getMessage()}      ``",
                "`` SQL语句: {$db->getRawSql()}                         ``",
                "`` 错误信息和参数详情:                                    ``",
                "````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'db');

            // 静态错误
            self::$error_['db_error'] = empty(self::$error_['db_error']) ?
                [$error->getMessage()] : array_merge(self::$error_['db_error'], [$error->getMessage()]);

            return false;
        }
    }

    /**
     * 检测用户编号是否是超管
     * @param null $roleId
     * @return bool
     */
    public static function isSuper($roleId = null)
    {

        return in_array($roleId, self::$superRoleId);
    }

    /**
     * 检测是否拥有某权限
     * @param array|string $action 方法
     *  - 如果是[route]字符串自行初始化
     * @param string $controller 控制器
     * @param string $module 模块
     * @param int $roleId
     * @return bool
     */
    public static function checkAuth($action, $controller = '', $module = '', $roleId = 0)
    {

        ### 初始化方法、控制器、模块信息
        // 如果是[route]模式
        if (strstr($action, '/')) {
            // 分割
            $action = explode('/', $action);
            // 赋值模块
            $module = count($action) == 2 ? null : $action[0];
            // 赋值控制器
            $controller = count($action) == 2 ? $action[0] : $action[1];
            // 赋值方法
            $action = end($action);
        }
        // 默认[控制器]当前访问控制器
        if (!$controller) $controller = \Yii::$app->controller->id;
        // 默认[模块名]当前访问模块
        if (!$module) $module = \Yii::$app->controller->module->id;

        ### 先检测路由是否存在
        if (!self::routeExist($module, $controller, $action)) return false;

        ### 默认数据
        // 默认[用户角色编号]当前登录用户角色编号
        /* @var AdminUserModel $user */
        $user = \Yii::$app->admin->identity;
        if (!$roleId) $roleId = $user->getAttribute('role_id');
        // 超管直接有权限
        if (self::isSuper($roleId)) return true;

        ### 合成路由
        // 路由
        $route = '/' . lcfirst($controller) . '/' . lcfirst($action);
        // [Yii]路优化
        $route = '/' . $module . Inflector::camel2id($route, '-', true);

        ### 检测角色是否拥有路由权限 -  就让他走缓存不行就清理缓存呗
        $roleModel = AdminRoleCacheModel::loadModel($roleId);
        // 角色不存在则不该拥有权限
        if (!$roleModel) return false;

        return $roleModel->hadRouteAuth($route);
    }

    /**
     * 路由是否存在
     * @param string $moduleId 模块
     * @param string $controllerId 控制器
     * @param string $actionId 方法
     * @return bool
     */
    protected static function routeExist($moduleId, $controllerId, $actionId)
    {
        try {

            // 检测模块
            $module = \Yii::$app->getModule($moduleId);
            if (empty($module)) return false;

            // 检测控制器
            $controller = $module->createControllerByID($controllerId);
            if (empty($controller)) return false;

            // 检测方法
            $actionId = Inflector::camel2id($actionId, '-', true);
            return $controller->createAction($actionId) || $actionId == '*' ? true : false;
        } catch (\Exception $exception) {
            return false;
        }
    }


    /**
     * 返回排序最大值
     * @return int
     */
    public static function getSortMax()
    {
        return self::$sortMax;
    }

    /**
     * 返回排序最小值
     * @return int
     */
    public static function getSortMin()
    {
        return self::$sortMin;
    }

    /**
     * 获取[正常]状态 值
     * @return mixed|string
     */
    public static function getStatusOpen()
    {

        $list = array_column(self::$statusList, null, 'key');
        return $list['open']['value'];
    }

    /**
     * 获取[禁用]状态 值
     * @return mixed|string
     */
    public static function getStatusDisabled()
    {

        $list = array_column(self::$statusList, null, 'key');
        return $list['disabled']['value'];
    }

    /**
     * 获取[状态]文本
     * @param $value
     * @return mixed|string
     */
    public static function getStatusText($value)
    {

        // 列表
        $list = array_column(self::$statusList, null, 'value');
        // 不合法 - 不存在
        if (empty($list[$value]['text']))

            return '--';

        // 最终正常返回
        return $list[$value]['text'];
    }

    /**
     * 获取[状态]列表 值
     * @return mixed|string
     */
    public static function getStatusList()
    {

        // 最终正常返回
        return self::$statusList;
    }
}
